home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-06-18 | 20.8 KB | 699 lines | [TEXT/MPS ] |
- PROGRAM Sample;
-
- USES
- MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf, Traps, Notification,
- {my custom stuff}
- Utilities;
-
- CONST
- {MPW 3.0 will include a Traps.p interface file that includes constants for trap numbers.
- These constants are from that file.}
- {1.02 - since using MPW 3.0 only, we include Traps.p, so we now have trap numbers.}
-
- {1.01 - changed constants to begin with 'k' for consistency, except for resource IDs}
- {SysEnvironsVersion is passed to SysEnvirons to tell it which version of the
- SysEnvRec we understand.}
- kSysEnvironsVersion = 1;
-
- {OSEvent is the event number of the suspend/resume and mouse-moved events sent
- by MultiFinder. Once we determine that an event is an osEvent, we look at the
- high byte of the message sent to determine which kind it is. To differentiate
- suspend and resume events we check the resumeMask bit.}
- kOSEvent = app4Evt; {event used by MultiFinder}
- kSuspendResumeMessage = 1; {high byte of suspend/resume event message}
- kResumeMask = 1; {bit of message field for resume vs. suspend}
- kNoEvents = 0; {no events mask}
-
- {1.01 - kMinHeap - This is the minimum result from the following
- equation:
-
- ORD(GetApplLimit) - ORD(ApplicZone)
-
- for the application to run. It will insure that enough memory will
- be around for reasonable-sized scraps, FKEYs, etc. to exist with the
- application, and still give the application some 'breathing room'.
- To derive this number, we ran under a MultiFinder partition that was
- our requested minimum size, as given in the 'SIZE' resource.}
-
- kMinHeap = 21 * 1024;
-
- {1.01 - kMinSpace - This is the minimum result from PurgeSpace, when called
- at initialization time, for the application to run. This number acts
- as a double-check to insure that there really is enough memory for the
- application to run, including what has been taken up already by
- pre-loaded resources, the scrap, code, and other sundry memory blocks.}
-
- kMinSpace = 8 * 1024;
-
- {kExtremeNeg and kExtremePos are used to set up wide open rectangles and regions.}
- kExtremeNeg = -32768;
- kExtremePos = 32767 - 1; {required for old region bug}
-
- {The following constants are all resource IDs, corresponding to resources in Sample.r.}
- rMenuBar = 128; {application's menu bar}
- rAboutAlert = 128; {about alert}
- rUserAlert = 129; {error user alert}
- rNotInstalledAlert = 130; {the extension isn’t installed}
-
- {The following constants are used to identify menus and their items. The menu IDs
- have an "m" prefix and the item numbers within each menu have an "i" prefix.}
- mApple = 128; {Apple menu}
- iAbout = 1;
-
- mFile = 129; {File menu}
- iNew = 1;
- iClose = 4;
- iQuit = 12;
-
- mEdit = 130; {Edit menu}
- iUndo = 1;
- iCut = 3;
- iCopy = 4;
- iPaste = 5;
- iClear = 6;
-
- {1.01 - kDITop and kDILeft are used to locate the Disk Initialization dialogs.}
- kDITop = $0050;
- kDILeft = $0070;
-
-
- VAR
- {The "g" prefix is used to emphasize that a variable is global.}
-
- {GMac is used to hold the result of a SysEnvirons call. This makes
- it convenient for any routine to check the environment. It is
- global information, anyway.}
- gMac : SysEnvRec; {set up by Initialize}
-
- {GHasWaitNextEvent is set at startup, and tells whether the WaitNextEvent
- trap is available. If it is false, we know that we must call GetNextEvent.}
- gHasWaitNextEvent : BOOLEAN; {set up by Initialize}
-
- {GInBackground is maintained by our osEvent handling routines. Any part of
- the program can check it to find out if it is currently in the background.}
- gInBackground : BOOLEAN; {maintained by Initialize and DoEvent}
-
- gNotifyRec: NMRec;
-
- {$S Initialize}
- FUNCTION TrapAvailable(tNumber: INTEGER; tType: TrapType): BOOLEAN;
-
- {Check to see if a given trap is implemented. This is only used by the
- Initialize routine in this program, so we put it in the Initialize segment.
- The recommended approach to see if a trap is implemented is to see if
- the address of the trap routine is the same as the address of the
- Unimplemented trap.}
- {1.02 - Needs to be called after call to SysEnvirons so that it can check
- if a ToolTrap is out of range of a pre-MacII ROM.}
-
- BEGIN
- IF (tType = ToolTrap) &
- (gMac.machineType > envMachUnknown) &
- (gMac.machineType < envMacII) THEN BEGIN {it's a 512KE, Plus, or SE}
- tNumber := BAND(tNumber, $03FF);
- IF tNumber > $01FF THEN {which means the tool traps}
- tNumber := _Unimplemented; {only go to $01FF}
- END;
- TrapAvailable := NGetTrapAddress(tNumber, tType) <>
- GetTrapAddress(_Unimplemented);
- END; {TrapAvailable}
-
-
- {$S Main}
- FUNCTION IsDAWindow(window: WindowPtr): BOOLEAN;
-
- {Check if a window belongs to a desk accessory.}
-
- BEGIN
- IF window = NIL THEN
- IsDAWindow := FALSE
- ELSE {DA windows have negative windowKinds}
- IsDAWindow := (WindowPeek(window)^.windowKind < 0);
- END; {IsDAWindow}
-
-
- {$S Main}
- FUNCTION IsAppWindow(window: WindowPtr): BOOLEAN;
-
- {Check to see if a window belongs to the application. If the window pointer
- passed was NIL, then it could not be an application window. WindowKinds
- that are negative belong to the system and windowKinds less than userKind
- are reserved by Apple except for windowKinds equal to dialogKind, which
- mean it is a dialog.
- 1.02 - In order to reduce the chance of accidentally treating some window
- as an AppWindow that shouldn't be, we'll only return true if the windowkind
- is userKind. If you add different kinds of windows to Sample you'll need
- to change how this all works.}
-
- BEGIN
- IF window = NIL THEN
- IsAppWindow := FALSE
- ELSE {application windows have windowKinds = userKind (8)}
- WITH WindowPeek(window)^ DO
- IsAppWindow := (windowKind = userKind);
- END; {IsAppWindow}
-
-
- {$S Main}
- PROCEDURE AlertUser;
-
- {Display an alert that tells the user an error occurred, then exit the program.
- This routine is used as an ultimate bail-out for serious errors that prohibit
- the continuation of the application. Errors that do not require the termination
- of the application should be handled in a different manner. Error checking and
- reporting has a place even in the simplest application. For simplicity, the alert
- displayed here only says that an error occurred, but not what it was. There are
- various methods available for being more specific.}
-
- VAR
- itemHit : INTEGER;
- BEGIN
- SetCursor(arrow);
- itemHit := Alert(rUserAlert, NIL);
- ExitToShell;
- END; {AlertUser}
-
- CONST
- SysZone = $2A6; {[GLOBAL VAR] Address of system heap zone
- system heap zone [pointer]}
- ApplZone = $2AA; {[GLOBAL VAR] Address of application heap zone
- application heap zone [pointer]}
-
- {$S Main}
- PROCEDURE DoCloseWindow(window: WindowPtr);
- BEGIN
- SetZone(SystemZone);
- KillPicture(GetWindowPic(window));
- SetZone(ApplicationZone);
- SetWindowPic(window,NIL);
- DisposeWindow(window);
- END; {DoCloseWindow}
-
-
-
- {$S Initialize}
- FUNCTION Gestalt(selector: OSType;VAR response: LONGINT): OSErr;
- EXTERNAL;
- FUNCTION NewGestalt(selector: OSType;gestaltFunction: ProcPtr): OSErr;
- EXTERNAL;
- FUNCTION ReplaceGestalt(selector: OSType;gestaltFunction: ProcPtr;VAR oldGestaltFunction: ProcPtr): OSErr;
- EXTERNAL;
-
- TYPE
-
- SharedGlobals = RECORD
- q: QHdr; {queue header}
- nmReqPtr: NMRecPtr; {notification manager record}
- END;
- SharedGlobalsPtr = ^SharedGlobals;
-
- VAR
- gSharedGlobals : SharedGlobalsPtr;
-
- PROCEDURE RememberWhereMyQueueIs;
- VAR
- err : OSErr;
- itemHit : INTEGER;
- BEGIN
- (*
- Ptr(gQueueHeader) := NewPtrSys(sizeof(QHdr));
-
- with gQueueHeader^ do begin
- qFlags:= 0;
- qHead:= nil;
- qTail:= nil;
- end;
-
- err := NewGestalt('Msg!',ProcPtr(gQueueHeader));
- *)
- err := Gestalt('Msg!',longint(gSharedGlobals));
- if err <> noErr then begin
- SetCursor(arrow);
- itemHit := Alert(rNotInstalledAlert, NIL);
- ExitToShell;
- end;
- END;
-
- {$S Initialize}
- PROCEDURE Initialize;
-
- VAR
- menuBar : Handle;
- window : WindowPtr;
- ignoreError : OSErr;
- total, contig : LongInt;
- ignoreResult : BOOLEAN;
- event : EventRecord;
- count : INTEGER;
-
- BEGIN
- gInBackground := FALSE;
-
- InitGraf(@thePort);
- InitFonts;
- InitWindows;
- InitMenus;
- TEInit;
- InitDialogs(NIL);
- InitCursor;
-
- {Call EventAvail so that we don't lose some important events.}
- FOR count := 1 TO 3 DO
- ignoreResult := EventAvail(everyEvent, event);
-
- menuBar := GetNewMBar(rMenuBar); {read menus into menu bar}
- SetMenuBar(menuBar); {install menus}
- DisposHandle(menuBar);
- AddResMenu(GetMHandle(mApple), 'DRVR'); {add DA names to Apple menu}
- DrawMenuBar;
-
- RememberWhereMyQueueIs;
- END; {Initialize}
-
- {$S Main}
- PROCEDURE Terminate;
-
- {Clean up the application and exits. We close all of the windows so that
- they can update their documents, if any.}
- VAR
- aWindow : WindowPtr;
- BEGIN
- WHILE FrontWindow <> NIL DO
- DoCloseWindow(FrontWindow); {close this window}
- ExitToShell; {exit if no cancellation}
- END; {Terminate}
-
-
- {$S Main}
- PROCEDURE MakeNewWindow( thePicture : PicHandle; dateTime : LONGINT;
- location : Rect; appName : Str255 );
- VAR
- theWindow : WindowPtr;
- timeNDateString : Str255;
- BEGIN
- if location.top < GetMBarHeight + 20 then
- OffsetRect(location, 0, GetMBarHeight + 20 - location.top);
- IUTimeString(dateTime, true, timeNDateString);
- timeNDateString := concat(appName,' ',timeNDateString);
- theWindow := NewWindow (nil, location, timeNDateString, false, documentProc,
- WindowPtr(-1), true, 0);
- SetWindowPic(theWindow, thePicture);
- ForceOnScreen( theWindow );
- ShowWindow( theWindow );
- END;
-
- TYPE
- ourQElem = record
- qLink: QElemPtr;
- qType: INTEGER;
- picture: PicHandle;
- dateTime: LONGINT;
- location: Rect;
- appName: Str255;
- end;
-
- {$S Main}
-
- PROCEDURE GetPicturesFromQueue;
- VAR
- err : OSErr;
- myQElement : ^ourQElem;
- BEGIN
- QElemPtr(myQElement) := gSharedGlobals^.q.qHead;
- if gSharedGlobals^.nmReqPtr <> NIL then BEGIN
- err := NMRemove(gSharedGlobals^.nmReqPtr);
- gSharedGlobals^.nmReqPtr := NIL;
- END;
- if myQElement <> NIL then begin
- err := Dequeue(QElemPtr(myQElement), @gSharedGlobals^.q);
- if (err = noErr) THEN BEGIN
- MakeNewWindow( myQElement^.picture, myQElement^.dateTime,
- myQElement^.location, myQElement^.appName);
- SetZone(SystemZone);
- DisposePtr(Ptr(myQElement));
- SetZone(ApplicationZone);
- END;
- END;
- END;
-
-
- {$S Main}
- PROCEDURE AdjustMenus;
-
- {Enable and disable menus based on the current state.
- The user can only select enabled menu items. We set up all the menu items
- before calling MenuSelect or MenuKey, since these are the only times that
- a menu item can be selected. Note that MenuSelect is also the only time
- the user will see menu items. This approach to deciding what enable/
- disable state a menu item has the advantage of concentrating all the decision-
- making in one routine, as opposed to being spread throughout the application.
- Other application designs may take a different approach that may or may not be
- just as valid.}
-
- VAR
- window : WindowPtr;
- menu : MenuHandle;
-
- BEGIN
- window := FrontWindow;
-
- menu := GetMHandle(mFile);
- IF IsDAWindow(window) THEN {we can allow desk accessories to be closed from the menu}
- EnableItem(menu, iClose)
- ELSE
- EnableItem(menu, iClose);
-
- EnableItem(menu, iNew);
-
- menu := GetMHandle(mEdit);
- IF IsDAWindow(window) THEN BEGIN {a desk accessory might need the edit menu}
- EnableItem(menu, iUndo);
- EnableItem(menu, iCut);
- EnableItem(menu, iCopy);
- EnableItem(menu, iPaste);
- EnableItem(menu, iClear);
- END ELSE BEGIN {but we know we do not}
- DisableItem(menu, iUndo);
- DisableItem(menu, iCut);
- DisableItem(menu, iCopy);
- DisableItem(menu, iClear);
- DisableItem(menu, iPaste);
- END;
- END; {AdjustMenus}
-
-
- {$S Main}
- PROCEDURE DoMenuCommand(menuResult: LONGINT);
-
- {This is called when an item is chosen from the menu bar (after calling
- MenuSelect or MenuKey). It performs the right operation for each command.
- It is good to have both the result of MenuSelect and MenuKey go to
- one routine like this to keep everything organized.}
-
- VAR
- menuID : INTEGER; {the resource ID of the selected menu}
- menuItem : INTEGER; {the item number of the selected menu}
- itemHit : INTEGER;
- daName : Str255;
- daRefNum : INTEGER;
- handledByDA : BOOLEAN;
- ignore : BOOLEAN;
- dateTime : LONGINT;
-
- BEGIN
- menuID := HiWrd(menuResult); {use built-ins (for efficiency)...}
- menuItem := LoWrd(menuResult); {to get menu item number and menu number}
- CASE menuID OF
- mApple:
- CASE menuItem OF
- iAbout: {bring up alert for About}
- itemHit := Alert(rAboutAlert, NIL);
- OTHERWISE BEGIN {all non-About items in this menu are DAs}
- GetItem(GetMHandle(mApple), menuItem, daName);
- daRefNum := OpenDeskAcc(daName);
- END;
- END;
- mFile:
- CASE menuItem OF
- iNew: BEGIN
- { GetDateTime(dateTime);
- MakeNewWindow(GetPicture(128), dateTime, nil, "");}
- END;
- iClose:
- if FrontWindow <> NIL then
- DoCloseWindow(FrontWindow);
- iQuit:
- Terminate;
- END;
- mEdit: {call SystemEdit for DA editing & MultiFinder}
- handledByDA := SystemEdit(menuItem-1); {since we don't do any editing}
- END;
- HiliteMenu(0); {unhighlight what MenuSelect (or MenuKey) hilited}
- END; {DoMenuCommand}
-
-
- {$S Main}
- PROCEDURE DrawWindow(window: WindowPtr);
-
- {Draw the contents of the application window. We do some drawing in color, using
- Classic QuickDraw's color capabilities. This will be black and white on old
- machines, but color on color machines. At this point, the window's visRgn is
- set to allow drawing only where it needs to be done.}
-
- BEGIN
- SetPort(window);
-
- END; {DrawWindow}
-
-
- {$S Main}
- PROCEDURE DoContentClick(window: WindowPtr; event: EventRecord);
- BEGIN
- END; {DoContentClick}
-
-
- {$S Main}
- PROCEDURE DoUpdate(window: WindowPtr);
-
- {This is called when an update event is received for a window.
- It calls DrawWindow to draw the contents of an application window.
- As an effeciency measure that does not have to be followed, it
- calls the drawing routine only if the visRgn is non-empty. This
- will handle situations where calculations for drawing or drawing
- itself is very time-consuming.}
-
- BEGIN
- IF IsAppWindow(window) THEN BEGIN
- BeginUpdate(window); {sets up the visRgn, clears updateRgn}
- IF NOT EmptyRgn(window^.visRgn) THEN {draw if updating needs to be done}
- DrawWindow(window);
- EndUpdate(window); {restores the visRgn}
- END;
- END; {DoUpdate}
-
-
- {$S Main}
- PROCEDURE DoActivate(window: WindowPtr; becomingActive: BOOLEAN);
-
- {This is called when a window is activated or deactivated.
- In Sample, the Window Manager's handling of activate and
- deactivate events is sufficient. Other applications may have
- TextEdit records, controls, lists, etc., to activate/deactivate.}
-
- BEGIN
- IF IsAppWindow(window) THEN
- IF becomingActive THEN
- {do whatever you need to at activation}
- ELSE
- {do whatever you need to at deactivation};
- END; {DoActivate}
-
-
- {$S Main}
- PROCEDURE GetGlobalMouse(VAR mouse: Point);
-
- {Get the global coordinates of the mouse. When you call OSEventAvail
- it will return either a pending event or a null event. In either case,
- the where field of the event record will contain the current position
- of the mouse in global coordinates and the modifiers field will reflect
- the current state of the modifiers. Another way to get the global
- coordinates is to call GetMouse and LocalToGlobal, but that requires
- being sure that thePort is set to a valid port.}
-
- VAR
- event : EventRecord;
-
- BEGIN
- IF OSEventAvail(kNoEvents, event) THEN; {we aren't interested in any events}
- mouse := event.where; {just the mouse position}
- END;
-
-
- {$S Main}
- PROCEDURE AdjustCursor(mouse: Point; region: RgnHandle);
-
- {Change the cursor's shape, depending on its position. This also calculates the region
- where the current cursor resides (for WaitNextEvent). If the mouse is ever outside of
- that region, an event is generated, causing this routine to be called. This
- allows us to change the region to the region the mouse is currently in. If
- there is more to the event than just “the mouse moved”, we get called before the
- event is processed to make sure the cursor is the right one. In any (ahem) event,
- this is called again before we fall back into WNE.}
-
-
- VAR
- window : WindowPtr;
- arrowRgn : RgnHandle;
- plusRgn : RgnHandle;
- globalPortRect : Rect;
-
-
- BEGIN
- window := FrontWindow; {we only adjust the cursor when we are in front}
- IF (NOT gInBackground) AND (NOT IsDAWindow(window)) THEN BEGIN
- {calculate regions for different cursor shapes}
- arrowRgn := NewRgn;
- plusRgn := NewRgn;
-
- {start with a big, big rectangular region}
- {1.01 - changed to kExtremeNeg and kExtremePos for consistency}
- SetRectRgn(arrowRgn, kExtremeNeg, kExtremeNeg,
- kExtremePos, kExtremePos);
-
- {calculate plusRgn}
- IF IsAppWindow(window) THEN BEGIN
- SetPort(window); {make a global version of the portRect}
- SetOrigin(-window^.portBits.bounds.left, -window^.portBits.bounds.top);
- globalPortRect := window^.portRect;
- RectRgn(plusRgn, globalPortRect);
- SectRgn(plusRgn, window^.visRgn, plusRgn);
- SetOrigin(0, 0);
- END;
-
- {subtract other regions from arrowRgn}
- DiffRgn(arrowRgn, plusRgn, arrowRgn);
-
- {change the cursor and the region parameter}
- IF PtInRgn(mouse, plusRgn) THEN BEGIN
- SetCursor(GetCursor(plusCursor)^^);
- CopyRgn(plusRgn, region);
- END ELSE BEGIN
- SetCursor(arrow);
- CopyRgn(arrowRgn, region);
- END;
-
- {get rid of our local regions}
- DisposeRgn(arrowRgn);
- DisposeRgn(plusRgn);
- END;
- END; {AdjustCursor}
-
-
- {$S Main}
- PROCEDURE DoEvent(event: EventRecord);
-
- {Do the right thing for an event. Determine what kind of event it is, and call
- the appropriate routines.}
-
- VAR
- part, err : INTEGER;
- window : WindowPtr;
- hit : BOOLEAN;
- key : CHAR;
- aPoint : Point;
-
- BEGIN
- CASE event.what OF
- mouseDown: BEGIN
- part := FindWindow(event.where, window);
- CASE part OF
- inMenuBar: BEGIN {process the menu command}
- AdjustMenus;
- DoMenuCommand(MenuSelect(event.where));
- END;
- inSysWindow: {let the system handle the mouseDown}
- SystemClick(event, window);
- inContent:
- IF window <> FrontWindow THEN BEGIN
- SelectWindow(window);
- {DoEvent(event);} {use this line for "do first click"}
- END ELSE
- DoContentClick(window, event);
- inDrag: {pass screenBits.bounds to get all gDevices}
- DragWindow(window, event.where, screenBits.bounds);
- inGrow:;
- inGoAway:
- IF TrackGoAway(window, event.where) THEN
- DoCloseWindow(window);
- inZoomIn, inZoomOut:;
- END;
- END;
- keyDown, autoKey: BEGIN {check for menukey equivalents}
- key := CHR(BAnd(event.message, charCodeMask));
- IF BAnd(event.modifiers, cmdKey) <> 0 THEN {Command key down}
- IF event.what = keyDown THEN BEGIN
- AdjustMenus; {enable/disable/check menu items properly}
- DoMenuCommand(MenuKey(key));
- END;
- END; {call DoActivate with the window and...}
- activateEvt: {TRUE for activate, FALSE for deactivate}
- DoActivate(WindowPtr(event.message), BAnd(event.modifiers, activeFlag) <> 0);
- updateEvt: {call DoUpdate with the window to update}
- BEGIN
- DoUpdate(WindowPtr(event.message));
- END;
- {1.01 - It is not a bad idea to at least call DIBadMount in response
- to a diskEvt, so that the user can format a floppy.}
- diskEvt:
- IF HiWrd(event.message) <> noErr THEN BEGIN
- SetPt(aPoint, kDILeft, kDITop);
- err := DIBadMount(aPoint, event.message);
- END;
- kOSEvent:
- CASE BAnd(BRotL(event.message, 8), $FF) OF {high byte of message}
- kSuspendResumeMessage: BEGIN
- DoActivate(FrontWindow, NOT gInBackground);
- END;
- END;
- nullEvent: BEGIN
- END;
- END;
- END; {DoEvent}
-
-
- {$S Main}
- PROCEDURE EventLoop;
-
- {Get events forever, and handle them by calling DoEvent.
- Get the events by calling WaitNextEvent, if it's available, otherwise
- by calling GetNextEvent. Also call AdjustCursor each time through the loop.}
-
- VAR
- cursorRgn : RgnHandle;
- gotEvent : BOOLEAN;
- event : EventRecord;
- mouse : Point;
-
- BEGIN
- cursorRgn := NewRgn; {we’ll pass WNE an empty region the 1st time thru}
- REPEAT
- GetPicturesFromQueue;
-
- IF gHasWaitNextEvent THEN BEGIN {put us 'asleep' forever under MultiFinder}
- GetGlobalMouse(mouse); {since we might go to sleep}
- AdjustCursor(mouse, cursorRgn);
- gotEvent := WaitNextEvent(everyEvent, event, 10, cursorRgn);
- END ELSE BEGIN
- SystemTask; {must be called if using GetNextEvent}
- gotEvent := GetNextEvent(everyEvent, event);
- END;
- IF gotEvent THEN BEGIN
- AdjustCursor(event.where, cursorRgn); {make sure we have the right cursor}
- DoEvent(event);
- END;
- UNTIL FALSE; {loop forever; we quit through an ExitToShell}
- END; {EventLoop}
-
-
- PROCEDURE _DataInit; EXTERNAL;
-
- {This routine is part of the MPW runtime library. This external
- reference to it is done so that we can unload its segment, %A5Init.}
-
- {$S Main}
- BEGIN
- UnloadSeg(@_DataInit); {note that _DataInit must not be in Main!}
-
- {1.01 - call to ForceEnvirons removed}
- {If you have stack requirements that differ from the default,
- then you could use SetApplLimit to increase StackSpace at
- this point, before calling MaxApplZone.}
-
- MaxApplZone; {expand the heap so code segments load at the top}
-
- Initialize; {initialize the program}
- UnloadSeg(@Initialize); {note that Initialize must not be in Main!}
-
- EventLoop; {call the main event loop}
- END.
-